#include "golImageAlgorithm.h"

#include "vtkCell.h"
#include "vtkPointData.h"
#include "vtkIndent.h"

vtkStandardNewMacro(golImageAlgorithm);

golImageAlgorithm::golImageAlgorithm()
{
	// current hardcoded game rules:
	// dead cells with 4 neighbors comes to life
	// living cells with 5 neighbors and age < 3 stays alive
	// all other living cells die (age, overcrowding, or lonliness)
	bornLow = 14;
	bornHigh = 26;
	liveLow = 14;
	liveHigh = 26;
	age=3;

	// Create a greyscale lookup table
	this->table = vtkLookupTable::New();
	this->table->SetRange(0, 255); // image intensity range
	this->table->SetNumberOfColors(2);
	this->table->SetValueRange(0.0, 1.0); // from black to white
	this->table->SetSaturationRange(0.0, 0.0); // no color saturation
	this->table->SetRampToLinear();
	this->table->Build();
}

golImageAlgorithm::~golImageAlgorithm()
{
	this->table->Delete();
}

void golImageAlgorithm::SimpleExecute(vtkImageData* input, vtkImageData* output)
{
	int numComponents = input->GetPointData()->GetNumberOfComponents();
	output->SetNumberOfScalarComponents(numComponents);
	output->SetExtent(input->GetExtent());

	// iterators
	int x, y, z, x2, y2, z2;

	// maximum and actual neighbor counts
	int nMax,nActual;

	// the scalar value
	double scalarValue;

	// offsets to start counting from
	int offXL,offXR,offYL,offYR,offZL,offZR; 

	// iterate over the input to map the values
	int d[3];
	input->GetDimensions(d);
	for(z = 0; z < d[2]; z++)
	{
		// start and stop offsets for neighbor search on z axis
		offZL=-1; offZR=1;
		if(z==0) offZL=0;
		if(z==(d[2]-1)) offZR=0;

		for(y = 0; y < d[1]; y++)
		{
			// start and stop offsets for neighbor search on y axis
			offYL=-1; offYR=1;
			if(y==0) offYL=0;
			if(y==(d[1]-1)) offYR=0;

			for(x = 0; x < d[0]; x++)
			{
				// start and stop offsets for neighbor search on z axis
				offXL=-1; offXR=1;
				if(x==0) offXL=0;
				if(x==(d[0]-1)) offXR=0;


				// count the neighbors using the offsets
				nActual = 0;	
				for(z2 = offZL; z2<=offZR; z2++)
				{
					for(y2 = offYL; y2<=offYR; y2++)
					{
						for(x2 = offXL; x2<=offXR; x2++)
						{
							if(!(x2==0 && y2==0 && z2==0))
							{
								// this should be fixed to support more than just black & white
								if(input->GetScalarComponentAsDouble(x+x2,y+y2,z+z2,0)>127) nActual++;
							}
						}
					}
				}

				// max number of neighbors this cell can have
				nMax = (((offXR-offXL+1)*(offYR-offYL+1)*(offZR-offZL+1)))-1;

				// adjust rules for actual max neighbors
				int tmp_bornLow=bornLow;
				int tmp_bornHigh=bornHigh;
				int tmp_liveLow=liveLow;
				int tmp_liveHigh=liveHigh;

				if(nMax!=26)
				{
					float adjustment=nMax/26.0;
					tmp_bornLow=(tmp_bornLow*adjustment)+1;
					tmp_bornHigh=(tmp_bornHigh*adjustment)+1;
					tmp_liveLow=(tmp_liveLow*adjustment)+1;
					tmp_liveHigh=(tmp_liveHigh*adjustment)+1;
				}

				// we should fix the hard coded values
				// now assumes only 2 colors (black/white,dead/alive)
				// scalar<128=dead,scalar>=128=alive
				scalarValue=input->GetScalarComponentAsDouble(x,y,z,0);
				if(scalarValue<128) // cell is dead, check if it will come alive
				{					
					if(nActual >= tmp_bornLow && nActual <= tmp_bornHigh)
					{
						// get the color from the map
						unsigned char *color = table->MapValue(255.0);
						// find out where to put the new color
						unsigned char *colorDest = (unsigned char*)output->GetScalarPointer(x,y,z);			
						// set the new color
						for(int i=0; i<numComponents;i++)
						{
							*colorDest++=*color++;
						}
					}
					else
					{
						// get the color from the map
						unsigned char *color = table->MapValue(0);
						// find out where to put the new color
						unsigned char *colorDest = (unsigned char*)output->GetScalarPointer(x,y,z);			
						// set the new color
						for(int i=0; i<numComponents;i++)
						{
							*colorDest++=*color++;
						}
					}
				}
				else // cell is alive, check if it stays alive or dies
				{
					if( nActual >= tmp_liveLow && nActual <= tmp_liveHigh )
						// aging disabled
						//&& (maxAge == 0 || universe[n][i][j][z] < maxAge)) // if maxAge == 0, cell's age doesn't matter
					{
						//if(maxAge) // if maxAge is 0, skip aging feature
						//{
						//	// increment age and set the color scale one fraction lower
						//	universe[n2][i][j][z] = universe[n][i][y][z] + 1;
						//	scalars->SetValue(offset, float(universe[n][i][y][z]) / maxAge);
						//}
						// get the color from the map
						unsigned char *color = table->MapValue(255.0);
						// find out where to put the new color
						unsigned char *colorDest = (unsigned char*)output->GetScalarPointer(x,y,z);			
						// set the new color
						for(int i=0; i<numComponents;i++)
						{
							*colorDest++=*color++;
						}
					}
					else
					{
						// get the color from the map
						unsigned char *color = table->MapValue(0);
						// find out where to put the new color
						unsigned char *colorDest = (unsigned char*)output->GetScalarPointer(x,y,z);			
						// set the new color
						for(int i=0; i<input->GetPointData()->GetNumberOfComponents();i++)
						{
							*colorDest++=*color++;
						}
					}
				}
			}
		}
	}
}
;

